package com.ruoyi.game.service.impl;

import java.io.IOException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.game.constant.GameErrorCode;
import com.ruoyi.game.constant.GameRoomProperties;
import com.ruoyi.game.controller.dto.CheckOpReq;
import com.ruoyi.game.controller.dto.CheckOpResp;
import com.ruoyi.game.core.*;
import com.ruoyi.game.core.card.BasePoker;
import com.ruoyi.game.core.exception.GameOpException;
import com.ruoyi.game.domain.GamePlayer;
import com.ruoyi.game.domain.GameRoom;
import com.ruoyi.game.service.GameRoomMemoryManager;
import com.ruoyi.game.service.IGamePlayerService;
import com.ruoyi.game.service.IGameSetService;
import com.ruoyi.game.websocket.handler.GameWebSocketHandler;
import com.ruoyi.game.websocket.message.testpacket.DealReq;
import com.ruoyi.game.websocket.session.SessionInfo;
import com.ruoyi.game.websocket.message.packet.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import com.ruoyi.game.mapper.GameRoomMapper;
import com.ruoyi.game.service.IGameRoomService;
import org.springframework.web.socket.WebSocketSession;

import javax.annotation.PostConstruct;

import static com.ruoyi.game.constant.GameErrorCode.*;

/**
 * 房间Service业务层处理
 * 
 * @author ruoyi
 * @date 2025-04-18
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class GameRoomServiceImpl implements IGameRoomService 
{
    private final GameRoomMapper gameRoomMapper;
    private final IGamePlayerService gamePlayerService;
    private final IGameSetService gameSetService;
    private final GameWebSocketHandler socketHandler;
    private final GameRoomProperties gameRoomProperties;
    private final GameRoomMemoryManager roomMemory;
    @PostConstruct
    public void cleanOrphanRooms() {
        log.info("[系统启动] 开始清理孤立房间");

        List<GameRoom> allRooms = gameRoomMapper.selectAllRooms();
        if (allRooms.isEmpty()) {
            log.info("[系统启动] 没有需要清理的房间");
            return;
        }

        for (GameRoom room : allRooms) {
            Long roomId = room.getId();
            if (!roomMemory.getRoomMap().containsKey(roomId)) {
                // 当前房间在内存中不存在，判定为孤立房间
                log.warn("[系统启动] 检测到孤立房间，删除 roomId={} roomCode={}", roomId, room.getRoomCode());
                gameRoomMapper.deleteGameRoomById(roomId);
            }
        }
    }

    /* ======================== 房间缓存 ======================== */
    @Override
    public GameRoomCore createRoom(Long dbId, String roomKey, boolean robotRoom) {
        GameRoomCore room = roomMemory.create(dbId, roomKey, socketHandler, this, gameSetService, gamePlayerService, gameRoomProperties, robotRoom);
        log.info("[RoomService] 创建房间: id={}, key={}, robot={}", dbId, roomKey, robotRoom);
        return room;
    }
    @Override
    public GameRoomCore getRoom(Long roomId) {
        return roomMemory.get(roomId);
    }

    @Override
    public void removeRoom(Long roomId) {
        roomMemory.remove(roomId);
        log.info("[RoomService] 移除房间: {}", roomId);
    }

    private GameRoomCore joinRoom(Long roomId, Long playerId, WebSocketSession session) throws IOException {
        GameRoomCore room = getRoom(roomId);
        if (room == null) throw new IllegalStateException("房间不存在");

        GamePlayer player = gamePlayerService.selectGamePlayerById(playerId);
        if (player == null) throw new IllegalStateException("玩家不存在");

        int seatIndex = room.join(player);
        GameWebSocketHandler.SESSION_INFO.put(session, new SessionInfo(playerId, roomId));
        log.info("[RoomService] 玩家 {} 加入房间 {}，座位 {}", playerId, roomId, seatIndex);
        return room;
    }

    public Collection<GameRoomCore> allRooms() {
        return roomMemory.all();
    }

    /* ------------------------------------------------------------ */
    /*                WebSocket 入口（由 GameWsDispatcher 调用）     */
    /* ------------------------------------------------------------ */

    /** JOIN_ROOM */
    public void join(WebSocketSession session, JoinRoomReq req) {
        log.info("[JOIN_ROOM] 请求参数：{}", req);
        try {
            // 1. 获取 playerId
            SessionInfo info = GameWebSocketHandler.getSessionInfo(session);
            if (info == null) {
                log.warn("[JOIN_ROOM] Session 未绑定 SessionInfo");
                socketHandler.sendError(session, SESSION_NOT_FOUND, "会话未绑定，请重新登录");
                return;
            }
            long playerId = info.getPlayerId();
            log.info("[JOIN_ROOM] playerId={} 加入房间 roomId={}", playerId, req.getRoomCode());

            // 2. 查询房间信息
            GameRoom gameRoom = selectByRoomCode(req.getRoomCode());
            if (gameRoom == null) {
                socketHandler.sendError(session, ROOM_NOT_FOUND, "房间不存在");
                return;
            }

            // 3. 获取内存中的房间对象
            GameRoomCore room = getRoom(gameRoom.getId());
            if (room == null) {
                log.warn("[JOIN_ROOM] 房间未初始化: roomId={}", gameRoom.getId());
                socketHandler.sendError(session, ROOM_NOT_FOUND, "房间未创建或已关闭");
                return;
            }

            // 4. 查询玩家信息
            GamePlayer player = gamePlayerService.selectGamePlayerById(playerId);
            if (player == null) {
                socketHandler.sendError(session, PLAYER_NOT_FOUND, "玩家不存在");
                return;
            }

            // 5. 校验房卡
            Long roomCard = player.getRoomCard() != null ? player.getRoomCard() : 0L;
            if (roomCard < 1) {
                socketHandler.sendError(session, ROOM_CARD_NOT_ENOUGH, "房卡不足，无法加入房间");
                return;
            }

            // 需要通过code获取room的id
            joinRoom(gameRoom.getId(), playerId, session);

            int seatIndex = room.findByPlayerId(playerId).getSeatIndex();

            log.info("[JOIN_ROOM] 广播 ROOM_UPDATE: playerId={} seatIndex={}", playerId, seatIndex);

        } catch (IOException e) {
            log.error("[JOIN_ROOM] 房间加入失败", e);
            socketHandler.sendError(session, BROADCAST_FAILED, "房间加入失败");
        }
    }

    /** READY / 取消准备 */
    public void ready(WebSocketSession session, ReadyReq req) {
        log.info("[READY] 请求参数：{}", req);
        SessionInfo info = GameWebSocketHandler.getSessionInfo(session);
        if (info == null) {
            log.error("[READY] Session 未绑定 SessionInfo");
            socketHandler.sendError(session, SESSION_NOT_FOUND, "会话未绑定，请重新登录");
            return;
        }

        long playerId = info.getPlayerId();
        GameRoomCore room;
        try {
            room = requireRoomByPlayerId(playerId);
        } catch (IllegalStateException e) {
            log.error("[READY] 玩家 {} 不在任何房间中", playerId);
            socketHandler.sendError(session, ROOM_NOT_FOUND, "未在任何房间中");
            return;
        }

        GameRoomPos pos;
        try {
            pos = room.findByPlayerId(playerId);
        } catch (IllegalStateException e) {
            log.error("[READY] 玩家 {} 在房间中找不到座位", playerId);
            socketHandler.sendError(session, ROOM_SEAT_NOT_FOUND, "座位信息异常");
            return;
        }

        int seatIndex = pos.getSeatIndex();

        try {
            room.ready(playerId, req.isReady());
            log.info("[READY] playerId={} 设置准备状态为 {}", playerId, req.isReady());
        } catch (IOException e) {
            log.error("[READY] 广播准备状态失败", e);
            socketHandler.sendError(session, BROADCAST_FAILED, "广播准备状态失败");
            return;
        }

        log.info("[READY] 广播 ROOM_UPDATE seatIndex={} ready={}", seatIndex, req.isReady());
    }

    @Override
    public void leave(WebSocketSession session, LeaveRoomReq req) {
        try {
            Long playerId = GameWebSocketHandler.getPlayerId(session);
            if (playerId == null) {
                log.warn("[WS] 离开房间失败，找不到玩家ID");
                socketHandler.sendError(session, PLAYER_OFFLINE, "未登录，无法离开房间");
                return;
            }

            Long roomId = req.getRoomId();
            GameRoomCore room = getRoom(roomId);
            if (room == null) {
                log.warn("[WS] 离开房间失败，房间不存在，roomId={}", roomId);
                socketHandler.sendError(session, ROOM_NOT_FOUND, "房间不存在");
                return;
            }

            room.leave(playerId);
            log.info("[WS] 玩家 {} 成功离开房间 {}", playerId, roomId);
        } catch (Exception e) {
            log.error("[WS] 玩家离开房间异常", e);
            socketHandler.sendError(session, LEAVE_ROOM_FAILED, "离开房间异常");
        }
    }

    @Override
    public void dissolve(WebSocketSession session, DissolveRoomReq req) {
        try {
            Long roomId = req.getRoomId();
            Long playerId = GameWebSocketHandler.getPlayerId(session);
            if (playerId == null) {
                socketHandler.sendError(session, PLAYER_OFFLINE, "未登录或登录已失效");
                return;
            }

            GameRoomCore room = getRoom(roomId);
            if (room == null) {
                socketHandler.sendError(session, ROOM_NOT_FOUND, "房间不存在");
                return;
            }

            // 结束当前对局
            if (room.isInGame()) {
                room.endSet();
            }

            // 踢出所有玩家
            room.kickOutPlayers();

            // ✅ 删除数据库房间记录
            deleteGameRoomById(roomId);
            log.info("[DISSOLVE] 房间 {} 已在数据库中删除", roomId);

            // 删除房间
            roomMemory.remove(roomId);

            log.info("[DISSOLVE] 玩家 {} 解散房间 {} 成功", playerId, roomId);
        } catch (Exception e) {
            log.error("[WS] 解散房间失败", e);
            socketHandler.sendError(session, DISSOLVE_ROOM_FAILED, "解散房间失败");
        }
    }

    /** 托管/取消托管 */
    public void trusteeship(WebSocketSession session, TrusteeshipReq req) {
        log.info("[TRUSTEESHIP] 请求参数：{}", req);
        SessionInfo info = GameWebSocketHandler.getSessionInfo(session);
        if (info == null) {
            log.warn("[TRUSTEESHIP] Session 未绑定 SessionInfo");
            socketHandler.sendError(session, SESSION_NOT_FOUND, "会话未绑定，请重新登录");
            return;
        }

        long playerId = info.getPlayerId();
        GameRoomCore room;
        try {
            room = requireRoomByPlayerId(playerId);
        } catch (IllegalStateException e) {
            log.error("[TRUSTEESHIP] 玩家 {} 不在任何房间中", playerId);
            socketHandler.sendError(session, ROOM_NOT_FOUND, "未在任何房间中");
            return;
        }

        try {
            room.trusteeship(playerId, req.isEnable());
            log.info("[TRUSTEESHIP] playerId={} 设置托管为 {}", playerId, req.isEnable());
        } catch (IOException e) {
            log.error("[TRUSTEESHIP] 广播托管状态失败", e);
            socketHandler.sendError(session, BROADCAST_FAILED, "托管状态广播失败");
            return;

        }

        GameRoomPos pos = room.findByPlayerId(playerId);
        int seatIndex = pos.getSeatIndex();
        socketHandler.broadcastPacketToRoom(room.getRoomId(), new WsPacket<>("TRUSTEESHIP",
                WsPackets.trusteeship(seatIndex, req.isEnable())));
        log.info("[TRUSTEESHIP] 广播 seatIndex={} enable={}", seatIndex, req.isEnable());
    }

    /** 聊天 */
    public void chat(WebSocketSession session, ChatReq req) {
        log.info("[CHAT] 请求参数：{}", req);
        SessionInfo info = GameWebSocketHandler.getSessionInfo(session);
        if (info == null) {
            log.warn("[CHAT] Session 未绑定 SessionInfo");
            socketHandler.sendError(session, SESSION_NOT_FOUND, "会话未绑定，请重新登录");
            return;
        }

        long playerId = info.getPlayerId();
        GameRoomCore room;
        try {
            room = requireRoomByPlayerId(playerId);
        } catch (IllegalStateException e) {
            log.error("[CHAT] 玩家 {} 不在任何房间中", playerId);
            socketHandler.sendError(session, ROOM_NOT_FOUND, "未在任何房间中");
            return;
        }

        GameRoomPos pos = room.findByPlayerId(playerId);
        int seatIndex = pos.getSeatIndex();

        socketHandler.broadcastPacketToRoom(room.getRoomId(), new WsPacket<>("CHAT",
                WsPackets.chat(seatIndex, req.getContent(), req.getQuickId())));
        log.info("[CHAT] 广播 seatIndex={} content={} quickId={}", seatIndex, req.getContent(), req.getQuickId());
    }

    @Override
    public void outPlayerCard(WebSocketSession session, OutCardReq req) throws IOException {
        log.info("[OUT_CARD] 请求参数：{}", req);
        SessionInfo info = GameWebSocketHandler.getSessionInfo(session);
        if (info == null) {
            log.warn("[OUT_CARD] Session 未绑定 SessionInfo");
            socketHandler.sendError(session, SESSION_NOT_FOUND, "会话未绑定，请重新登录");
            return;
        }
        long playerId = info.getPlayerId();
        GameRoomCore room;
        try {
            room = requireRoomByPlayerId(playerId);
        } catch (IllegalStateException e) {
            log.error("[OUT_CARD] 玩家 {} 不在任何房间中", playerId);
            socketHandler.sendError(session, ROOM_NOT_FOUND, "未在任何房间中");
            return;
        }

        room.opPlayerCard(playerId, req.getOpType(), req.getCards());
        log.info("[OUT_CARD] playerId={} 出牌成功 opType={}", playerId, req.getOpType());
    }

    /** 新增：根据 playerId 查找所在的房间 */
    public GameRoomCore requireRoomByPlayerId(Long playerId) {
        for (GameRoomCore room : allRooms()) {
            if (room.containsPlayer(playerId)) {
                return room;
            }
        }
        throw new GameOpException(GameErrorCode.PLAYER_NOT_IN_ROOM, "\"玩家 \" + playerId + \" 不在任何房间中\"!");
    }

    @Override
    public GameRoom selectGameRoomById(Long id) {
        log.info("[DB] 查询房间 ID: {}", id);
        return gameRoomMapper.selectGameRoomById(id);
    }

    @Override
    public GameRoom selectGameRoomByCode(String roomCode) {
        return gameRoomMapper.selectGameRoomByCode(roomCode);
    }


    @Override
    public List<GameRoom> selectGameRoomList(GameRoom query) {
        log.info("[DB] 查询房间列表: {}", query);

        List<GameRoom> roomList = new ArrayList<>();

        for (GameRoomCore roomCore : roomMemory.all()) {
            GameRoom gameRoom = selectGameRoomById(roomCore.getRoomId()); // 查询数据库基础信息
            if (gameRoom == null) continue;

            // ✅ 补充实时人数
            int realPlayerCount = (int) roomCore.getSeats().stream()
                    .filter(pos -> !pos.isEmpty())
                    .count();
            gameRoom.setCurrentPlayerNum(realPlayerCount);

            // ✅ 可加额外查询条件，比如状态过滤
            if (query.getStatus() != null && !query.getStatus().equals(gameRoom.getStatus())) {
                continue;
            }
            if (query.getOwnerId() != null && !query.getOwnerId().equals(gameRoom.getOwnerId())) {
                continue;
            }

            roomList.add(gameRoom);
        }

        // ✅ 排序逻辑：未满的房间优先，人多优先
        roomList.sort((r1, r2) -> {
            boolean full1 = r1.getCurrentPlayerNum() >= r1.getPlayerNum();
            boolean full2 = r2.getCurrentPlayerNum() >= r2.getPlayerNum();

            if (full1 != full2) {
                return full1 ? 1 : -1; // 未满在前
            } else {
                return Integer.compare(r2.getCurrentPlayerNum(), r1.getCurrentPlayerNum()); // 人多在前
            }
        });

        log.info("[DB] 查询完成，活跃房间数={}", roomList.size());
        return roomList;
    }

    @Override
    public int insertGameRoom(GameRoom gameRoom) {
        gameRoom.setCreateTime(DateUtils.getNowDate());
        int result = gameRoomMapper.insertGameRoom(gameRoom);
        log.info("[DB] 插入房间: {}, result={}", gameRoom, result);
        return result;
    }

    @Override
    public int updateGameRoom(GameRoom gameRoom) {
        gameRoom.setUpdateTime(DateUtils.getNowDate());
        int result = gameRoomMapper.updateGameRoom(gameRoom);
        log.info("[DB] 更新房间: {}, result={}", gameRoom, result);
        return result;
    }

    @Override
    public int deleteGameRoomByIds(Long[] ids) {
        log.info("[DB] 批量删除房间: {}", (Object) ids);
        return gameRoomMapper.deleteGameRoomByIds(ids);
    }

    @Override
    public int deleteGameRoomById(Long id) {
        log.info("[DB] 删除房间 ID: {}", id);
        return gameRoomMapper.deleteGameRoomById(id);
    }

    @Override
    public boolean existsByRoomCode(String roomCode) {
        boolean exists = gameRoomMapper.countByRoomCode(roomCode) > 0;
        log.info("[DB] 检查房间码是否存在: {}, result={}", roomCode, exists);
        return exists;
    }

    @Override
    public GameRoom selectByRoomCode(String roomCode) {
        log.info("[DB] 查询房间码: {}", roomCode);
        return gameRoomMapper.selectByRoomCode(roomCode);
    }

    @Override
    public int deleteExpiredWaitingRooms() {
        int result = gameRoomMapper.deleteExpiredWaitingRooms();
//        log.info("[RoomCleanup] 已清理无人过期房间数量: {}", result);
        return result;
    }
    @Override
    public void mockShuffleAndDeal(WebSocketSession session, DealReq req) {
        try {
            Long playerId = GameWebSocketHandler.getPlayerId(session);
            if (playerId == null) {
                log.warn("[TEST_DEAL] 无法找到玩家ID");
                socketHandler.sendError(session, GameErrorCode.PLAYER_NOT_FOUND, "玩家未登录，无法测试发牌");
                return;
            }

            long testRoomId = 99999L; // 你可以用固定房间号，或者随机
            GameRoomCore room = new GameRoomCore(testRoomId, "123456", socketHandler, this, gameSetService, gamePlayerService, gameRoomProperties, false);

            room.mockShuffleAndDeal(session);
            log.info("[TEST_DEAL] 玩家 {} 测试发牌成功，房间 {}", playerId, room.getRoomId());
        } catch (Exception e) {
            log.error("[TEST_DEAL] 测试发牌异常", e);
            socketHandler.sendError(session, GameErrorCode.DEAL_ERROR, "测试发牌异常");
        }
    }

    public CheckOpResp checkCardOp(CheckOpReq req, Long playerId) {
        PKOpTypeReq opTypeReq = req.getOpType();
        List<Integer> cards = req.getCards();

        log.info("[CheckCardOp] 开始执行出牌校验，playerId={}，请求opType={}，cards={}",
                playerId, opTypeReq, BasePoker.toCardString(cards));

        GameRoomCore room = requireRoomByPlayerId(playerId);
        GameSetCore set = room.getCurrentSet();
        if (set == null) {
            log.warn("[CheckCardOp] 对局未开始，playerId={}", playerId);
            throw new GameOpException(GameErrorCode.GAME_NOT_FOUND, "对局未开始");
        }

        GameRoomPos roomPos = room.findByPlayerId(playerId);
        if (roomPos == null) {
            log.warn("[CheckCardOp] 未找到房间中的玩家，playerId={}", playerId);
            throw new GameOpException(GameErrorCode.PLAYER_NOT_FOUND, "未找到玩家");
        }

        int seatIndex = roomPos.getSeatIndex();
        PKOpType realOpType = GameRoomCore.mapOpType(seatIndex, set, opTypeReq, cards);
        log.info("[CheckCardOp] 映射后的实际操作类型为：{}", realOpType);

        GameSetPos setPos = set.getPlayerMap().get(seatIndex);
        if (setPos == null) {
            log.warn("[CheckCardOp] 未找到对局座位 seatIndex={}，playerId={}", seatIndex, playerId);
            throw new GameOpException(GameErrorCode.PLAYER_NOT_FOUND, "未找到对局座位");
        }

        CheckOpResp resp = new CheckOpResp();
        try {
            // ✅ 第一层：检查当前是否允许该操作类型
            log.info("[CheckCardOp] 校验玩家 seat={} 是否可以执行操作类型={}", seatIndex, realOpType);
            set.canOperate(seatIndex, realOpType);

            // ✅ 第二层：检查牌是否合法
            log.info("[CheckCardOp] 执行出牌合法性校验，cards={}（{}）",
                    cards, BasePoker.toCardString(cards));

            boolean valid = setPos.getCardTypeChecker().check(setPos, realOpType, cards, set);
            resp.setValid(valid);
            resp.setMessage(valid ? "校验通过" : "校验失败");

            log.info("[CheckCardOp] 校验完成，结果：valid={}，message={}", valid, resp.getMessage());
        } catch (GameOpException e) {
            log.warn("[CheckCardOp] 校验失败：{}", e.getMessage(), e);
            resp.setValid(false);
            resp.setMessage(e.getMessage());
        }

        return resp;
    }

}
